#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
端口控制器模块
功能：端口连接管理、基本通信功能、继电器控制逻辑、开机检测功能
"""

import serial
import serial.tools.list_ports
import time
from typing import Dict, List, Optional, Tuple, Any
import os
from src.managers.config_manager import config_manager

class SerialController:
    """端口控制器类"""
    def __init__(self):
        self.serial_port = None
        self.is_connected = False
        
        # 继电器控制配置
        self.relay_control_enabled = False  # 继电器控制开关
        self.relay_active_level = True     # True=高电平激活, False=低电平激活
        self.relay_open_before_loop = True  # 循环开始前是否开启继电器
        self.relay_close_after_loop = True  # 循环结束后是否关闭继电器
        self.relay_open_delay_ms = 100     # 继电器开启后延时(ms)
        self.relay_close_delay_ms = 100     # 继电器关闭前延时(ms)
        
        # 开机检测配置
        self.boot_check_enabled = False    # 开机检测开关
        self.boot_keyword = "Lierda"       # 开机完成关键字
        self.boot_timeout_ms = 5000        # 开机检测超时(ms)
    
    def list_available_ports(self) -> List[str]:
        """列出所有可用的端口"""
        ports = serial.tools.list_ports.comports()
        return [port.device for port in ports]
    
    def list_available_ports_with_info(self) -> List[Dict[str, Any]]:
        """列出所有可用的端口及其详细信息"""
        ports = serial.tools.list_ports.comports()
        port_info = []
        
        for port in ports:
            info = {
                'device': port.device,
                'description': port.description or '未知设备',
                'hwid': port.hwid or '未知硬件ID',
                'manufacturer': port.manufacturer or '未知制造商'
            }
            port_info.append(info)
        
        return port_info
    
    def connect(self, port: str, baudrate: int = 115200, timeout: float = 1.0) -> bool:
        """连接到端口"""
        try:
            # 检查端口是否可用
            available_ports = self.list_available_ports()
            if port not in available_ports:
                print(f"❌ 端口 {port} 当前不可用")
                return False
                
            self.serial_port = serial.Serial(
                port=port,
                baudrate=baudrate,
                timeout=timeout,
                bytesize=serial.EIGHTBITS,
                parity=serial.PARITY_NONE,
                stopbits=serial.STOPBITS_ONE
            )
            self.is_connected = True
            print(f"✅ 成功连接到端口 {port}，波特率 {baudrate}")
            return True
        except serial.SerialException as e:
            if "PermissionError" in str(e) or "Access is denied" in str(e):
                print(f"❌ 端口 {port} 被占用或权限不足")
                print("💡 可能原因：")
                print("   - 端口被其他程序占用")
                print("   - 需要管理员权限")
                print("   - 端口已断开连接")
            else:
                print(f"❌ 端口连接失败: {e}")
            return False
        except Exception as e:
            print(f"❌ 连接失败: {e}")
            return False

    def interactive_port_selection(self) -> Optional[str]:
        """交互式端口选择 - 直接输入端口号数字
        
        Returns:
            str: 选择的端口名称，如果用户取消则返回None
        """
        while True:
            ports_info = self.list_available_ports_with_info()
            if not ports_info:
                print("❌ 没有找到可用的端口，正在重新检测...")
                time.sleep(2)  # 短暂等待后自动重试
                continue
            
            print("\n📡 可用端口:")
            print("=" * 50)
            port_map = {}
            for port_info in ports_info:
                device = port_info['device']
                # 提取数字部分，如COM3 -> 3
                port_num = ''.join(filter(str.isdigit, device))
                port_map[port_num] = device
                print(f"{port_num} - {device} - {port_info['description']}")
            print("=" * 50)
            print("💡 提示: 直接输入端口号数字(如 3)或输入 0 取消")
            
            try:
                choice = input("\n请输入端口号数字: ").strip()
                if choice == '0':
                    return None
                
                # 验证输入的端口号数字是否有效
                if choice in port_map:
                    return port_map[choice]
                else:
                    available_nums = list(port_map.keys())
                    print(f"❌ 端口 {choice} 当前不可用")
                    print("可用的端口号数字:", ", ".join(available_nums))
                    
            except KeyboardInterrupt:
                print("\n❌ 用户取消")
                return None
            except Exception as e:
                print(f"❌ 输入错误: {e}")
    

    
    def connect_with_baudrate_selection(self, port: str) -> bool:
        """连接端口时选择波特率"""
        baudrates = [9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600]
        
        print(f"\n📡 为端口 {port} 选择波特率:")
        print("="*40)
        for i, baud in enumerate(baudrates, 1):
            print(f"{i}. {baud} bps")
        print("9. 自定义波特率")
        
        try:
            choice = int(input("请选择波特率 (1-9): ").strip())
            if choice == 9:
                baudrate = int(input("请输入自定义波特率: ").strip())
            elif 1 <= choice <= len(baudrates):
                baudrate = baudrates[choice - 1]
            else:
                baudrate = 115200  # 默认
                
            return self.connect(port, baudrate)
        except ValueError:
            print("❌ 无效输入，使用默认波特率 115200")
            return self.connect(port, 115200)
    
    def disconnect(self):
        """断开端口连接"""
        if self.serial_port and self.serial_port.is_open:
            # 断开前关闭继电器
            if self.relay_control_enabled:
                self.set_relay_state(False)
            self.serial_port.close()
            self.is_connected = False
            print("✅ 端口连接已关闭")
    
    def configure_relay_control(self):
        """配置继电器控制参数"""
        print("\n🔌 继电器控制配置")
        print("=" * 30)
        
        # 是否启用继电器控制
        current_state = "启用" if self.relay_control_enabled else "禁用"
        print(f"当前状态: {current_state}")
        choice = input("是否启用继电器控制? (y/n): ").strip().lower()
        self.relay_control_enabled = choice == 'y'
        
        if self.relay_control_enabled:
            # 设置激活电平
            current_level = "高电平" if self.relay_active_level else "低电平"
            print(f"当前激活电平: {current_level}")
            level_choice = input("继电器激活电平? (h=高电平, l=低电平): ").strip().lower()
            self.relay_active_level = level_choice != 'l'
            
            # 循环前开启继电器
            current_open_before = "是" if self.relay_open_before_loop else "否"
            print(f"循环前开启继电器: {current_open_before}")
            open_choice = input("循环开始前是否开启继电器? (y/n): ").strip().lower()
            self.relay_open_before_loop = open_choice == 'y'
            
            # 循环后关闭继电器
            current_close_after = "是" if self.relay_close_after_loop else "否"
            print(f"循环后关闭继电器: {current_close_after}")
            close_choice = input("循环结束后是否关闭继电器? (y/n): ").strip().lower()
            self.relay_close_after_loop = close_choice == 'y'
            
            # 继电器开启后延时
            print(f"当前继电器开启后延时: {self.relay_open_delay_ms}ms")
            open_delay_input = input("继电器开启后延时(ms, 默认100): ").strip()
            if open_delay_input:
                try:
                    self.relay_open_delay_ms = max(10, int(open_delay_input))  # 最小10ms
                except ValueError:
                    self.relay_open_delay_ms = 100
            
            # 继电器关闭前延时
            print(f"当前继电器关闭前延时: {self.relay_close_delay_ms}ms")
            close_delay_input = input("继电器关闭前延时(ms, 默认100): ").strip()
            if close_delay_input:
                try:
                    self.relay_close_delay_ms = max(10, int(close_delay_input))  # 最小10ms
                except ValueError:
                    self.relay_close_delay_ms = 100
            
            # 开机检测配置
            current_boot_check = "是" if self.boot_check_enabled else "否"
            print(f"开机检测: {current_boot_check}")
            boot_check_choice = input("是否启用开机检测? (y/n): ").strip().lower()
            self.boot_check_enabled = boot_check_choice == 'y'
            
            if self.boot_check_enabled:
                print(f"当前开机关键字: '{self.boot_keyword}'")
                keyword_input = input("开机检测关键字(默认Lierda): ").strip()
                if keyword_input:
                    self.boot_keyword = keyword_input
                
                print(f"当前开机超时: {self.boot_timeout_ms}ms")
                timeout_input = input("开机检测超时(ms, 默认5000): ").strip()
                if timeout_input:
                    try:
                        self.boot_timeout_ms = max(1000, int(timeout_input))  # 最小1秒
                    except ValueError:
                        self.boot_timeout_ms = 5000
            
            print(f"✅ 继电器控制已配置完成")
            print(f"   状态: {'启用' if self.relay_control_enabled else '禁用'}")
            print(f"   激活电平: {'高电平' if self.relay_active_level else '低电平'}")
            print(f"   循环前开启: {'是' if self.relay_open_before_loop else '否'}")
            print(f"   循环后关闭: {'是' if self.relay_close_after_loop else '否'}")
            print(f"   开启后延时: {self.relay_open_delay_ms}ms")
            print(f"   关闭前延时: {self.relay_close_delay_ms}ms")
            print(f"   开机检测: {'启用' if self.boot_check_enabled else '禁用'}")
            if self.boot_check_enabled:
                print(f"   开机关键字: '{self.boot_keyword}'")
                print(f"   开机超时: {self.boot_timeout_ms}ms")
        else:
            print("✅ 继电器控制已禁用")
    
    def set_relay_state(self, state: bool):
        """设置继电器状态
        Args:
            state: True=开启继电器, False=关闭继电器
        """
        if not self.relay_control_enabled or not self.serial_port or not self.serial_port.is_open:
            return
        
        try:
            # 根据激活电平和期望状态计算实际电平
            actual_level = self.relay_active_level == state
            self.serial_port.dtr = actual_level
            
            status = "开启" if state else "关闭"
            level = "高电平" if actual_level else "低电平"
            print(f"🔌 继电器已{status} (DTR={level})")
            
        except Exception as e:
            print(f"❌ 继电器控制失败: {e}")
    
    def toggle_relay(self):
        """切换继电器状态"""
        if not self.relay_control_enabled:
            print("⚠️ 继电器控制未启用")
            return
        
        try:
            current_state = self.serial_port.dtr if self.relay_active_level else not self.serial_port.dtr
            new_state = not current_state
            self.set_relay_state(new_state)
        except Exception as e:
            print(f"❌ 切换继电器失败: {e}")
    
    def wait_for_boot_complete(self) -> bool:
        """等待开机完成
        Returns:
            True: 开机成功，收到关键字
            False: 开机超时，未收到关键字
        """
        if not self.boot_check_enabled or not self.is_connected:
            return True  # 如果未启用检测，直接返回成功
        
        print(f"⏳ 等待开机完成，检测关键字: '{self.boot_keyword}'")
        print(f"   超时时间: {self.boot_timeout_ms}ms")
        
        start_time = time.time()
        timeout_seconds = self.boot_timeout_ms / 1000.0
        
        # 清空端口缓冲区
        if self.serial_port.in_waiting > 0:
            self.serial_port.read(self.serial_port.in_waiting)
        
        while time.time() - start_time < timeout_seconds:
            try:
                if self.serial_port.in_waiting > 0:
                    data = self.serial_port.read(self.serial_port.in_waiting).decode('utf-8', errors='ignore')
                    # 转换为小写进行不区分大小写的检测
                    if self.boot_keyword.lower() in data.lower():
                        print(f"✅ 开机成功，收到关键字: '{self.boot_keyword}'")
                        return True
                
                # 小延时避免CPU占用过高
                time.sleep(0.1)
                
            except Exception as e:
                print(f"❌ 读取端口数据失败: {e}")
                return False
        
        print(f"⚠️ 开机超时，未收到关键字: '{self.boot_keyword}'")
        return False
    
    def send_command(self, command: str, delay_ms: int = 0) -> str:
        """发送AT指令并返回响应
        Args:
            command: 要发送的AT指令
            delay_ms: 发送后等待的毫秒数
        Returns:
            str: 端口响应内容
        """
        if not self.is_connected or not self.serial_port or not self.serial_port.is_open:
            return "❌ 未连接端口"
        
        try:
            # 清空接收缓冲区
            if self.serial_port.in_waiting > 0:
                self.serial_port.read(self.serial_port.in_waiting)
            
            # 发送指令
            full_command = command + '\r\n'
            self.serial_port.write(full_command.encode('utf-8'))
            
            # 等待延时
            if delay_ms > 0:
                time.sleep(delay_ms / 1000.0)
            
            # 读取响应
            response = ""
            timeout = 3.0  # 3秒超时
            start_time = time.time()
            
            while time.time() - start_time < timeout:
                if self.serial_port.in_waiting > 0:
                    response += self.serial_port.read(self.serial_port.in_waiting).decode('utf-8', errors='ignore')
                    # 如果收到响应，可以稍微多等一点确保完整
                    if "OK" in response or "ERROR" in response or len(response.strip()) > 0:
                        time.sleep(0.1)  # 额外等待100ms确保完整响应
                        # 再次检查是否有更多数据
                        if self.serial_port.in_waiting > 0:
                            response += self.serial_port.read(self.serial_port.in_waiting).decode('utf-8', errors='ignore')
                        break
                time.sleep(0.05)  # 小延迟避免CPU占用过高
            
            return response.strip()
        except Exception as e:
            return f"❌ 发送指令失败: {e}"
    
    def load_relay_config(self):
        """从配置文件加载继电器控制配置"""
        config = config_manager.load_config('relay')
        
        # 如果配置为空，使用默认配置
        if not config:
            config = config_manager.get_default_config('relay')
        
        # 更新属性
        self.relay_control_enabled = config.get('relay_control_enabled', False)
        self.relay_active_level = config.get('relay_active_level', True)
        self.relay_open_before_loop = config.get('relay_open_before_loop', True)
        self.relay_close_after_loop = config.get('relay_close_after_loop', True)
        self.relay_open_delay_ms = config.get('relay_open_delay_ms', 100)
        self.relay_close_delay_ms = config.get('relay_close_delay_ms', 100)
        self.boot_check_enabled = config.get('boot_check_enabled', False)
        self.boot_keyword = config.get('boot_keyword', "Lierda")
        self.boot_timeout_ms = config.get('boot_timeout_ms', 5000)
        
    def stop_all_relays(self):
        """停止所有继电器操作，保持当前状态"""
        try:
            if not self.relay_control_enabled or not self.serial_port or not self.serial_port.is_open:
                print("⚠️ 继电器控制未启用或端口未连接")
                return False
            
            # 保持继电器当前状态（不主动改变，只停止后续操作）
            print("🛑 继电器操作已停止，保持当前状态")
            return True
            
        except Exception as e:
            print(f"❌ 停止继电器失败: {e}")
            return False
    
    def save_relay_config(self):
        """保存继电器控制配置到文件"""
        config = {
            'relay_control_enabled': self.relay_control_enabled,
            'relay_active_level': self.relay_active_level,
            'relay_open_before_loop': self.relay_open_before_loop,
            'relay_close_after_loop': self.relay_close_after_loop,
            'relay_open_delay_ms': self.relay_open_delay_ms,
            'relay_close_delay_ms': self.relay_close_delay_ms,
            'boot_check_enabled': self.boot_check_enabled,
            'boot_keyword': self.boot_keyword,
            'boot_timeout_ms': self.boot_timeout_ms
        }
        
        success = config_manager.save_config('relay', config)
        if success:
            print("✅ 继电器配置已保存")
        else:
            print("❌ 继电器配置保存失败")